package com.xiefengf.elasticearch_template;

import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.aggregations.*;
import co.elastic.clients.elasticsearch._types.query_dsl.*;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.json.JsonData;
import com.xiefengf.es.Product;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.client.elc.NativeQuery;
import org.springframework.data.elasticsearch.client.elc.NativeQueryBuilder;
import org.springframework.data.elasticsearch.core.AggregationsContainer;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.*;
import org.springframework.data.elasticsearch.core.query.highlight.Highlight;
import org.springframework.data.elasticsearch.core.query.highlight.HighlightField;
import org.springframework.data.elasticsearch.core.query.highlight.HighlightParameters;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;


/**
 * @author xiefengf
 * @version 1.0
 * @date 2024/3/25 13:48
 *
 * 已弃用此测试类
 * 此测试类使用 需要修改ElasticSearchConfig配置类 ElasticsearchRestTemplate--> ElasticsearchTemplate
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class EsTest_ElasticsearchTemplate {

    @Resource
    private ElasticsearchTemplate elasticsearchTemplate;

    /**
     * 创建索引
     */
    @Test
    public void createIndex() {
        // 索引操作器
        IndexOperations indexOps = elasticsearchTemplate.indexOps(Product.class);
        // 是否存在，存在则先删除
        if (indexOps.exists()) {
            indexOps.delete();
        }
        // 创建索引
        boolean indexBool = indexOps.create();
        // 根据实体类获取映射关系
        Document mapping = indexOps.createMapping(Product.class);
        // 将映射关系添加至索引中
        boolean mappingBool = indexOps.putMapping(mapping);
        System.out.println(indexBool && mappingBool);
    }

    /**
     * 删除索引
     */
    @Test
    public void deleteIndex() {
        IndexOperations indexOps = elasticsearchTemplate.indexOps(Product.class);
        boolean bool = indexOps.delete();
        System.out.println(bool);
    }


    /**
     * 创建文档
     */
    @Test
    public void createDoc() {
        Product product = new Product(1001L, "小米手机", "手机", 2099.0, "http://www.atguigu/hw.jpg");
        Product save = elasticsearchTemplate.save(product);
        System.out.println(save);
    }

    /**
     * 批量创建文档
     */
    @Test
    public void createBatchDoc() {
        List<Product> productList = new ArrayList<>();
        productList.add(new Product(1002L, "小米手环", "手环", 399.0, "http://www.atguigu/hw.jpg"));
        productList.add(new Product(1003L, "红米手机", "手机", 1999.0, "http://www.atguigu/hw.jpg"));
        productList.add(new Product(1004L, "华为手机", "手机", 6999.0, "http://www.atguigu/hw.jpg"));
        productList.add(new Product(1005L, "华为手环", "手环", 599.0, "http://www.atguigu/hw.jpg"));
        productList.add(new Product(1006L, "苹果手机", "手机", 6999.0, "http://www.atguigu/hw.jpg"));
        productList.add(new Product(1007L, "苹果手环", "手环", 2099.0, "http://www.atguigu/hw.jpg"));
        productList.add(new Product(1008L, "三星电视", "电视", 10999.0, "http://www.atguigu/hw.jpg"));
        productList.add(new Product(1009L, "小米电视", "电视", 7999.0, "http://www.atguigu/hw.jpg"));
        Iterable<Product> iterable = elasticsearchTemplate.save(productList);
        System.out.println(iterable);
    }

    /**
     * 更新文档
     */
    @Test
    public void updateDoc() {
        Product product = new Product(1001L, "小米手机", "手机", 7999.0, "http://www.baidu/hw.jpg");
        Product save = elasticsearchTemplate.save(product);
        System.out.println(save);
    }

    /**
     * 根据id删除文档
     */
    @Test
    public void deleteDoc() {
        // 删除
        elasticsearchTemplate.delete("1001", Product.class);
    }

    /**
     * 根据条件删除文档
     */
    @Test
    public void deleteConditionDoc() {
        BoolQuery.Builder boolQueryBuilder = new BoolQuery.Builder();
        boolQueryBuilder.must(new Query.Builder().term(new TermQuery.Builder().value(FieldValue.of("手机")).field("category").build()).build());
        Query build = new Query.Builder().bool(boolQueryBuilder.build()).build();
        NativeQuery query = new NativeQueryBuilder().withQuery(build).build();
        // 删除
        ByQueryResponse response = elasticsearchTemplate.delete(query, Product.class);
        System.out.println(response.getDeleted());
    }

    /**
     * 根据id查询文档
     */
    @Test
    public void getById() {
        // 查询
        Product product = elasticsearchTemplate.get("1003", Product.class);
        System.out.println(product);
    }

    /**
     * 查询文档集合
     */
    @Test
    public void list() {
        org.springframework.data.elasticsearch.core.query.Query query = org.springframework.data.elasticsearch.core.query.Query.findAll();
        List<SearchHit<Product>> list = elasticsearchTemplate.search(query, Product.class).toList();
        System.out.println(list);
    }

    /**
     * 条件查询文档集合-1
     * {
     *  "query": {
     *      "term": {
     *          "category": {
     *              "value": "手环"
     *          }
     *      }
     *  }
     * }
     */
    @Test
    public void listByCondition1() {
        // 创建一个布尔查询构建器
        BoolQuery.Builder boolQueryBuilder = new BoolQuery.Builder();

        // 创建一个词查询，匹配 "category" 字段值为 "手环" 的文档
        TermQuery termQuery = new TermQuery.Builder().value(FieldValue.of("手环")).field("category").build();
        // 将词查询封装在一个 Query 对象中
        Query query1 = new Query.Builder().term(termQuery).build();

        // 将 query1 添加到布尔查询的 must 子句中，表示所有子查询必须匹配
        BoolQuery boolQuery = boolQueryBuilder.must(query1).build();
        // 将布尔查询封装在一个 Query 对象中
        Query query2 = new Query.Builder().bool(boolQuery).build();

        // 创建一个 NativeQuery 对象，用于执行原生查询
        NativeQuery nativeQuery = new NativeQueryBuilder().withQuery(query2).build();
        // 条件查询
        SearchHits<Product> search = elasticsearchTemplate.search(nativeQuery, Product.class);
        // 输出结果
        System.out.println(search.toList());
    }

    /**
     * 条件查询文档集合-2
     * {
     *     "query": {
     *         "match": {
     *             "category": "手机"
     *         }
     *     }
     * }
     */
    @Test
    public void listByCondition2() {
        // 创建一个布尔查询构建器
        BoolQuery.Builder boolQueryBuilder = new BoolQuery.Builder();

        // 创建一个匹配查询，匹配 "title" 字段中包含 "小米" 的文档
        MatchQuery matchQuery = new MatchQuery.Builder().field("title").query("小米").build();
        // 将匹配查询封装在一个 Query 对象中
        Query query1 = new Query.Builder().match(matchQuery).build();

        // 将 query1 添加到布尔查询的 must 子句中，表示所有子查询必须匹配
        BoolQuery boolQuery = boolQueryBuilder.must(query1).build();
        // 将布尔查询封装在一个 Query 对象中
        Query query2 = new Query.Builder().bool(boolQuery).build();

        // 创建一个 NativeQuery 对象，用于执行原生查询
        NativeQuery nativeQuery = new NativeQueryBuilder().withQuery(query2).build();
        // 执行条件查询，搜索 Product 类型的文档
        SearchHits<Product> search = elasticsearchTemplate.search(nativeQuery, Product.class);
        // 输出结果
        System.out.println(search.toList());
    }

    /**
     * 条件查询文档集合-3
     * {
     *     "query": {
     *         "match": {
     *             "title": "小米"
     *         }
     *     }
     * }
     */
//    https://blog.csdn.net/nsa65223/article/details/131701136
//    https://blog.csdn.net/weixin_45410366/article/details/123937817
    @Test
    public void listByCondition3() {
        // 创建一个布尔查询构建器
        BoolQuery.Builder boolQueryBuilder = new BoolQuery.Builder();

        // 创建一个 MatchQuery 对象，用于模糊匹配 "category" 字段
        MatchQuery matchQuery = new MatchQuery.Builder().field("category").query("电视").build();
        Query query1 = new Query.Builder().match(matchQuery).build();
        // 创建一个 MatchPhraseQuery 对象，用于完全匹配 "title" 字段
        MatchPhraseQuery matchPhraseQuery = new MatchPhraseQuery.Builder().field("title").query("华为手机").build();
        Query query2 = new Query.Builder().matchPhrase(matchPhraseQuery).build();
        // 将 MatchQuery 和 MatchPhraseQuery 添加到 BoolQuery 的 should 子句中
        boolQueryBuilder.should(query1).should(query2);

        // 创建一个 RangeQuery 对象，用于过滤价格大于 5000 的文档
        RangeQuery rangeQuery = new RangeQuery.Builder().field("price").gt(JsonData.of(100)).build();
        Query query3 = new Query.Builder().range(rangeQuery).build();
        // 将 RangeQuery 添加到 BoolQuery 的 filter 子句中
        boolQueryBuilder.filter(query3);

        // 创建query对象
        Query query4 = new Query.Builder().bool(boolQueryBuilder.build()).build();
        // 设置分页参数
        PageRequest pageRequest = PageRequest.of(0, 5);
        // 设置排序规则（按价格降序排列）
        Sort sort = Sort.by(Sort.Direction.DESC,"price");
        // 设置要显示的字段（两个参数分别对应包含和不包含）
        SourceFilter filter = new FetchSourceFilter(new String[]{"title","category","price"},new String[]{"images"});
        // 设置高亮字段
        HighlightParameters highlightParameters = HighlightParameters.builder().withPreTags(new String[]{"<strong>"}).withPostTags(new String[]{"</strong>"}).build();
        HighlightQuery highlightQuery = new HighlightQuery(new Highlight(highlightParameters,Collections.singletonList(new HighlightField("category"))),null);
        // 设置聚合函数
//        Aggregation aggregation = new Aggregation.Builder().valueCount(new ValueCountAggregation.Builder().field("id").build()).build();
        Aggregation aggregation = new Aggregation.Builder().avg(new AverageAggregation.Builder().field("price").build()).build();
        // 创建一个 RangeQuery 对象，并将其设置为包含指定的 Query 对象
        NativeQuery nativeQuery = new NativeQueryBuilder()
                .withPageable(pageRequest)
                .withQuery(query4)
                .withSourceFilter(filter)
                .withSort(sort)
                .withHighlightQuery(highlightQuery)
                .withAggregation("avg",aggregation)
                .build();
        // 执行查询
        SearchHits<Product> search = elasticsearchTemplate.search(nativeQuery, Product.class,IndexCoordinates.of("shopping"));
        // 输出结果
        AggregationsContainer<?> aggregations = search.getAggregations();
        List<Object> aggregations1 = (List<Object>) aggregations.aggregations();
        for (Object agg : aggregations1) {

        }
        // 输出结果
        for (SearchHit<Product> productSearchHit : search) {
            System.out.println(productSearchHit);
        }

    }

    /**
     * 当前函数查询第一页 每页显示100条数据,并且name有张三的数据
     */
    @Test
    public void testSearch() {
        BoolQuery.Builder boolQueryBuilder = new BoolQuery.Builder();
        boolQueryBuilder.must(new co.elastic.clients.elasticsearch._types.query_dsl.Query.Builder().term(new TermQuery.Builder().value(FieldValue.of("小米手机")).field("title").build()).build());
        co.elastic.clients.elasticsearch._types.query_dsl.Query build = new co.elastic.clients.elasticsearch._types.query_dsl.Query.Builder().bool(boolQueryBuilder.build()).build();
        NativeQueryBuilder query = new NativeQueryBuilder()
                .withPageable(PageRequest.of(1, 100))
                .withQuery(build)
                .withSourceFilter(new FetchSourceFilter(new String[]{}, new String[]{"@version", "@timestamp", "host", "tags"}))
                .withAggregation("count", new Aggregation.Builder().valueCount(new ValueCountAggregation.Builder().field("_id").build()).build());
        SearchHits<Product> test = elasticsearchTemplate.search(query.build(), Product.class, IndexCoordinates.of("shopping"));
        System.out.println(test.toList());
    }

}
